﻿using Prism.Events;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
using Xcj.Mes.Core.Logger;
using Xcj.Mes.Core.NotifyPropertyChanged;
using Xcj.Mes.WholeLine.DataBase.Entity;
using Xcj.Mes.WholeLine.DataBase.Service;
using Xcj.Mes.WholeLine.Plc.Events;
using Xcj.Mes.WholeLine.Plc.Factory;
using Xcj.Mes.WholeLine.Plc.Interface;

namespace Xcj.Mes.WholeLine.Plc.Base
{
    public class PlcBase : NotifyPropertyChangedBase, IPlc 
    {
        public static IEventAggregator _eventAggregator { private get; set; }

        private CancellationTokenSource _cts = new CancellationTokenSource();
        private string _deviceType = string.Empty;   // 设备类型 钉卷 组立 老化 座板 其它
        private string _name       = string.Empty;   // PLC名字
        private bool _isConnected  = false;          // Plc是否已连接
        private PlcConfigEntity _plcConfigEntity;        
            
        public CancellationTokenSource cts
        {
            get { return _cts;  }
            set { _cts = value; }
        }

        public string Name          // PLC名字
        {
            get => _name;
            set { _name = value; RaisePropertyChanged(); }
        }

        public string Brand        // 品牌
        {
            get => PlcConfigEntity.PlcBrand;
        }

        public string Model         // 型号
        {
            get => PlcConfigEntity.PlcModel;
        }

        public string Protocol      // 通信协议
        {
            get => PlcConfigEntity.PlcProtocol;
        }

        public string IP            // IP地址
        {
            get => PlcConfigEntity.PlcIP;
        }

        public int Port           // 通信端口
        {
            get => PlcConfigEntity.PlcPort;
        }

        public bool IsConnected     // Plc是否已连接
        {
            get => _isConnected;
            protected set { _isConnected = value; RaisePropertyChanged(); PlcFactory.OnPlcStatusChanged(_deviceType); }
        }

        protected string DeviceType
        {
            get => _deviceType;
            private set { _deviceType = value; RaisePropertyChanged(); }
        }

        private object LockWrite = new object();

        protected List<(string address, object value)> ListPreWrite { get; set; } = new List<(string address, object value)>();

        protected List<(string address, object value)> ListWriting { get; set; } = new List<(string address, object value)>();

        public PlcConfigEntity PlcConfigEntity 
        { 
            get => _plcConfigEntity; 
            set
            {
                _plcConfigEntity = value;
                RaisePropertyChanged();
                RaisePropertyChanged(nameof(Brand));
                RaisePropertyChanged(nameof(Model));
                RaisePropertyChanged(nameof(Protocol));
                RaisePropertyChanged(nameof(IP));
                RaisePropertyChanged(nameof(Port));
            }
        }

        public List<IGrouping<string, PlcAddressEntity>> ListPlcAddress { get; private set; } = new List<IGrouping<string, PlcAddressEntity>>();

        public Dictionary<string, PlcAddressEntity> DictPlcAddress { get; private set; } = new Dictionary<string, PlcAddressEntity>();

        protected object lockAccess = new object();
        
        protected Mutex Mutex = new Mutex();

        /// <summary>
        /// 易损件数据
        /// </summary>
        Dictionary<int, int> DicQuickWearPart = new Dictionary<int, int>();
        // 产量数据、报警数据

        public PlcBase(string deviceType)
        {
            DeviceType = deviceType?.Trim();
            if (string.IsNullOrEmpty(DeviceType)) return;

            #region 初始化设备各类特定数据
            int _QuickWearPartItemCount = 0;
            switch (DeviceType)
            {
                case "钉卷":
                    _QuickWearPartItemCount = 5;
                    break;
                case "组立":
                    _QuickWearPartItemCount = 5;
                    break;
                case "老化":
                    _QuickWearPartItemCount = 5;
                    break;
                case "座板":
                    _QuickWearPartItemCount = 5;
                    break;
            }                                                  

            for (int j = 0; j < _QuickWearPartItemCount; j++)
            {
                DicQuickWearPart[j] = 0;
            }
            #endregion
            Init();

            //Task.Run(RunLoop);

            Task.Run(MockRunLoop);
        }

        public void Init()
        {
            lock(lockAccess)
            {
                try
                {
                    // 读数据库，初始化地址列表
                    using (PlcAddressService service = new PlcAddressService())
                    {
                        //var datas = service?.All()?.Where(o => o.DeviceType == DeviceType)?.OrderBy(o => o.Address)?.GroupBy(o => o.DataType)?.ToList();
                        var datas = service?.All()?
                        .Where(o => o.DeviceType == DeviceType)?
                        .OrderBy(o => o.Address)?
                        .ToList()?  // 或者使用 AsEnumerable()，这里将数据加载到内存
                        .GroupBy(o => o.DataType)?
                        .ToList();

                        if ((datas?.Count ?? 0) <= 0) return;

                        ListPlcAddress = datas;
                        MyLogger.Log($"{DeviceType}");
                        foreach (var group in datas)
                        {
                            MyLogger.Log($"DataType: {group.Key} ,包含 {group.Count()} 项");
                        }
                        //当前DataType记录到日志
                        foreach (var data in datas)
                        {
                            foreach (var item in data)
                            {
                                if (DictPlcAddress.ContainsKey(item.Address)) continue;
                                DictPlcAddress.Add(item.Address, item);
                            }
                        }
                    }
                }
                catch { }
            };
        }

        private void MockRunLoop()
        {
            int _UsedCount = 1000;

            while (true)
            {
                Thread.Sleep(5000);
                #region 易损件消耗数据
                switch (DeviceType)
                { 
                    case "钉卷":
                        _UsedCount += 10;
                        break;
                    case "组立":
                        _UsedCount += 20;
                        break;
                    case "老化":
                        _UsedCount += 40;
                        break;
                    case "座板":
                        _UsedCount += 80;
                        break;
                }
                if (_UsedCount > 10000)
                {
                    _UsedCount = 1000;
                }
                for (int j = 0; j < DicQuickWearPart.Count; j++)
                {
                    DicQuickWearPart[j] = _UsedCount;
                }
                _eventAggregator.GetEvent<QuickWearPartDataEvent>().Publish((DeviceType, DicQuickWearPart)); 
                #endregion


            }
        }

        protected virtual void RunLoop()
        {
            DateTime[] dateTime = new DateTime[DictPlcAddress.Count];
            for (int i = 0; i < dateTime.Length; i++)
                dateTime[i] = DateTime.Now;

            // 是否需要重连
            bool IsNeedReconnect = false;

            while (true)
            {
                Thread.Sleep(100);

                if (cts?.Token.IsCancellationRequested??true) break;

                if ((!IsConnected) || IsNeedReconnect)
                {
                    using (PlcConfigService service = new PlcConfigService())
                    {
                        var data = service?.All()?.Where(o => o.DeviceType == DeviceType)?.OrderByDescending(o => o.CreateTime)?.FirstOrDefault();
                        if (data == null)
                        {
                            Thread.Sleep(2000);
                            continue;
                        }
                        this.PlcConfigEntity = data;
                    }

                    Connect();
                    
                    if (!IsConnected)
                    {
                        MyLogger.Log($"PLC未连接-{DeviceType}-{IP}:{Port}");
                        Thread.Sleep(2000);
                        continue;
                    }

                    IsNeedReconnect = false;
                }

                int j = 0;
                foreach(var group in ListPlcAddress)
                {
                    foreach (PlcAddressEntity entity in group)
                    {
                        if (!(entity.Mode?.Contains("读") ?? false))
                            continue;

                        if (string.IsNullOrEmpty(entity.Address))
                            continue;

                        if (!entity.HasPeriod) continue;

                        if ((DateTime.Now - dateTime[j]).TotalMilliseconds >= entity.Period)
                        {
                            switch (entity.DataType?.ToLower())
                            {
                                case "bool":
                                    if (!Read(entity.Address, 1, out byte[] v1)) continue;
                                    if ((v1?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v1[0] > 0 ? "True" : "False";
                                    break;

                                case "byte":
                                    if (!Read(entity.Address, 1, out byte[] v2)) continue;
                                    if ((v2?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v2[0].ToString();
                                    break;

                                case "int16":
                                    if (!Read(entity.Address, 1, out Int16[] v3)) continue;
                                    if ((v3?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v3[0].ToString();
                                    break;

                                case "uint16":
                                    if (!Read(entity.Address, 1, out UInt16[] v4)) continue;
                                    if ((v4?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v4[0].ToString();
                                    break;

                                case "int32":
                                    if (!Read(entity.Address, 1, out Int32[] v5)) continue;
                                    if ((v5?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v5[0].ToString();
                                    break;

                                case "uint32":
                                    if (!Read(entity.Address, 1, out UInt32[] v6)) continue;
                                    if ((v6?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v6[0].ToString();
                                    break;

                                case "int64":
                                    if (!Read(entity.Address, 1, out Int64[] v7)) continue;
                                    if ((v7?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v7[0].ToString();
                                    break;

                                case "uint64":
                                    if (!Read(entity.Address, 1, out UInt64[] v8)) continue;
                                    if ((v8?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v8[0].ToString();
                                    break;

                                case "float":
                                    if (!Read(entity.Address, 1, out float[] v9)) continue;
                                    if ((v9?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v9[0].ToString();
                                    break;

                                case "double":
                                    if (!Read(entity.Address, 1, out float[] v10)) continue;
                                    if ((v10?.Length ?? 0) <= 0) { IsNeedReconnect = true; continue; }
                                    entity.Value = v10[0].ToString();
                                    break;

                                case "string":
                                    if (entity.DataLength <= 0) break;
                                    if (!Read(entity.Address, (ushort)entity.DataLength, out string v11)) 
                                    { 
                                        IsNeedReconnect = true; 
                                        continue; 
                                    }
                                    entity.Value = v11;
                                    break;

                                default:
                                    break;
                            }

                            IsNeedReconnect = false;
                        }

                        dateTime[j++] = DateTime.Now;

                        //MyLogger.Log($"{entity.DeviceType}-{entity.Address}-{entity.Value}");
                        // 发布事件
                        (string DeviceType, string address, string value) plcData = (entity.DeviceType, entity.Address, entity.Value);
                        _eventAggregator.GetEvent<PlcDataEvent>().Publish(plcData);
                    }
                }
            }
        }

        public virtual bool Connect()
        {
            if ((string.IsNullOrEmpty(IP)) || (Port <= 0)) return false;

            if (Mutex?.WaitOne(0) ?? false)
            {
                lock (lockAccess)
                {
                    try
                    {
                        Disconnect(); 
                        IsConnected = false;
                    }
                    catch { }
                }
            }
            return false;
        }

        public virtual bool Connect(string ip, ushort port)
        {
            this.PlcConfigEntity.PlcIP = ip;
            this.PlcConfigEntity.PlcPort = port;
            return Connect();
        }

        public virtual async Task<bool> ConnectAsync()
        {
            return await Task.Run(() =>
            {
                return Connect();
            });
        }

        public async Task<bool> ConnectAsync(string ip, ushort port)
        {
            if ((string.IsNullOrEmpty(IP)) || (Port <= 0)) return false;
            this.PlcConfigEntity.PlcIP = ip;
            this.PlcConfigEntity.PlcPort = port;
            return await ConnectAsync();
        }

        public virtual void Disconnect() { }

        public virtual async Task DisconnectAsync(){ await Task.Run(Disconnect); }

        public async Task<T> ReadAsync<T>(string nStartAddr, ushort nCount)
        {
            return await Task.Run(() =>
            {
                switch (typeof(T).FullName)
                {
                    case "System.Boolean[]":
                        {
                            if (!Read(nStartAddr, nCount, out bool[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }
                    
                    case "System.Byte[]":
                        {
                            if (!Read(nStartAddr, nCount, out byte[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.Int16[]":
                        {
                            if (!Read(nStartAddr, nCount, out Int16[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.UInt16[]":
                        {
                            if (!Read(nStartAddr, nCount, out UInt16[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.Int32[]":
                        {
                            if (!Read(nStartAddr, nCount, out Int32[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.UInt32[]":
                        {
                            if (!Read(nStartAddr, nCount, out UInt32[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.Int64[]":
                        {
                            if (!Read(nStartAddr, nCount, out Int64[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.UInt64[]":
                        {
                            if (!Read(nStartAddr, nCount, out UInt64[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.Single[]":
                        {
                            if (!Read(nStartAddr, nCount, out float[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    case "System.Double[]":
                        {
                            if (!Read(nStartAddr, nCount, out double[] values))
                                return default(T);      // 修改这里
                            return (T)(object)values;   // 需要将 values 转换为 T                        }
                        }

                    //case "System.String[]":
                    //    {
                    //        if (!Read(nStartAddr, nCount, out string[] values))
                    //            return default(T);      // 修改这里
                    //        return (T)(object)values;   // 需要将 values 转换为 T                        }
                    //    }

                    default:
                        throw new NotSupportedException($"Type {typeof(T)} is not supported.");
                }
            });
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out bool[] values)
        {
            values = default;
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out byte[] values)
        {
            values = default;
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out Int16[] values)
        {
            values = default(Int16[]);
            try
            {
                if(!Read(nStartAddr, (ushort)(nCount*2), out byte[] bytes)) return false;
                values = new Int16[nCount];
                for (int i=0; i< nCount; i++)
                {
                    byte[] bt = new byte[2] { bytes[i*2], bytes[i*2+1] };
                    values[i] = BitConverter.ToInt16(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out UInt16[] values)
        {
            values = default(UInt16[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 2), out byte[] bytes)) return false;
                values = new UInt16[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[2] { bytes[i * 2], bytes[i * 2 + 1] };
                    values[i] = BitConverter.ToUInt16(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out Int32[] values)
        {
            values = default(Int32[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 4), out byte[] bytes)) return false;
                values = new Int32[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[] { bytes[i * 4], bytes[i * 4 + 1], bytes[i * 4 + 2], bytes[i * 4 + 3] };
                    values[i] = BitConverter.ToInt32(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out UInt32[] values)
        {
            values = default(UInt32[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 4), out byte[] bytes)) return false;
                values = new UInt32[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[] { bytes[i * 4], bytes[i * 4 + 1], bytes[i * 4 + 2], bytes[i * 4 + 3] };
                    values[i] = BitConverter.ToUInt32(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out Int64[] values)
        {
            values = default(Int64[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 8), out byte[] bytes)) return false;
                values = new Int64[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[] 
                    {
                        bytes[i * 8],   bytes[i * 8 + 1], bytes[i * 8 + 2], bytes[i * 8 + 3],
                        bytes[i * 8+4], bytes[i * 8 + 5], bytes[i * 8 + 6], bytes[i * 8 + 7]
                    };
                    values[i] = BitConverter.ToInt64(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out UInt64[] values)
        {
            values = default(UInt64[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 8), out byte[] bytes)) return false;
                values = new UInt64[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[]
                    {
                        bytes[i * 8],   bytes[i * 8 + 1], bytes[i * 8 + 2], bytes[i * 8 + 3],
                        bytes[i * 8+4], bytes[i * 8 + 5], bytes[i * 8 + 6], bytes[i * 8 + 7]
                    };
                    values[i] = BitConverter.ToUInt64(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out float[] values)
        {
            values = default(float[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 4), out byte[] bytes)) return false;
                values = new float[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[]
                    {
                        bytes[i * 4], bytes[i * 4 + 1], bytes[i * 4 + 2], bytes[i * 4 + 3],
                    };
                    values[i] = BitConverter.ToSingle(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort nCount, out double[] values)
        {
            values = default(double[]);
            try
            {
                if (!Read(nStartAddr, (ushort)(nCount * 8), out byte[] bytes)) return false;
                values = new double[nCount];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bt = new byte[]
                    {
                        bytes[i * 8],   bytes[i * 8 + 1], bytes[i * 8 + 2], bytes[i * 8 + 3],
                        bytes[i * 8+4], bytes[i * 8 + 5], bytes[i * 8 + 6], bytes[i * 8 + 7]
                    };
                    values[i] = BitConverter.ToDouble(bt, 0);
                }
                return true;
            }
            catch { }
            return false;
        }

        public virtual bool Read(string nStartAddr, ushort length, out string value)
        {
            value = string.Empty;
            try
            {
                if (!Read(nStartAddr, length, out byte[] bytes)) return false;
                value = Encoding.UTF8.GetString(bytes)?.Trim()?.Replace("\0", "")?.Replace("\r", "")?.Replace("\n", "");
                return true;
            }
            catch { }
            return false;
        }

        public async Task<bool> WriteAsync<T>(string nStartAddr, ushort count, T datas)
        {
            return await Task.Run(() =>
            {
                switch (typeof(T).FullName)
                {
                    case "System.Boolean[]":
                        return Write(nStartAddr, count, (bool[])(object)datas);

                    case "System.Byte[]":
                        return Write(nStartAddr, count, (byte[])(object)datas);

                    case "System.Int16[]":
                        return Write(nStartAddr, count, (Int16[])(object)datas);

                    case "System.UInt16[]":
                        return Write(nStartAddr, count, (UInt16[])(object)datas);

                    case "System.Int32[]":
                        return Write(nStartAddr, count, (Int32[])(object)datas);

                    case "System.UInt32[]":
                        return Write(nStartAddr, count, (UInt32[])(object)datas);

                    case "System.Int64[]":
                        return Write(nStartAddr, count, (Int64[])(object)datas);

                    case "System.UInt64[]":
                        return Write(nStartAddr, count, (UInt64[])(object)datas);

                    case "System.Single[]":
                        return Write(nStartAddr, count, (float[])(object)datas);

                    case "System.Double[]":
                        return Write(nStartAddr, count, (double[])(object)datas);

                    //case "System.String[]":
                    //    return Write(nStartAddr, count, (string[])(object)datas);

                    default:
                        throw new NotSupportedException($"Type {typeof(T)} is not supported.");
                }
            });
        }

        public virtual bool Write(string nStartAddr, ushort count, bool[] datas)
        {
            return false;
        }

        public virtual bool Write(string nStartAddr, ushort count, byte[] datas)
        {
            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, Int16[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 2];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 2 * i, 2);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, UInt16[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 2];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 2 * i, 2);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, int[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 4];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 4 * i, 4);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, uint[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 4];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 4 * i, 4);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, Int64[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 8];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 8 * i, 8);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, UInt64[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 8];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 8 * i, 8);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, float[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 4];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 4 * i, 4);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, ushort nCount, double[] values)
        {
            if ((nCount <= 0) || ((values?.Length ?? 0) < nCount))
                return false;

            try
            {
                byte[] _values = new byte[nCount * 8];
                for (int i = 0; i < nCount; i++)
                {
                    byte[] bys = BitConverter.GetBytes(values[i]);
                    Buffer.BlockCopy(bys, 0, _values, 8 * i, 8);
                }

                return Write(nStartAddr, (ushort)_values.Length, _values);
            }
            catch { }

            return false;
        }

        public virtual bool Write(string nStartAddr, string value)
        {
            if (string.IsNullOrEmpty(value?.Trim()))
                return false;

            try
            {
                byte[] values = Encoding.ASCII.GetBytes(value);
                return Write(nStartAddr, (ushort)values.Length, values);
            }
            catch { }

            return false;
        }

        public void Write(string address, object value)
        {
            lock (lockAccess)
                ListPreWrite.Add((address, value));
        }

        public void Dispose()
        {
            Task.Run(async () =>
            {
                try { _cts.Cancel(); } catch { }
                await DisconnectAsync();
            });
        }
    }
}
